// Jasc is just another script compressor!
// More info at http://jasc.codeplex.com
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Latest changes:
//
// * 1.3.1
// - New EndSemicolons parameter (es), adds a semicolon to the end of javascript/statements if true.
// - Updated Ajax Minifier to 4.55.
// - Minor bug fixes.
//
// * 1.2
// - New! DirectCompression parameter (dc), will compress source files directly instead of merging everything.
// - New! CopyOriginal parameter (co), which keeps a copy of original source file with a .original extension.
// - Because of the new CopyOriginal parameter, saving the .original is now disabled by default.
// - Updated Ajax Minifier to 4.25 (general bug fixes and some performance tweaks).
//
// * 1.1
// - New! Original merged file will be saved with .original extension.
// - New! If list is true (source is file list) and no src is passed, it will assume the current path/jasc.txt as the default file list.
// - Removed option va (CompressVariableNames), which is now a part of op (Optimize Code).
// - Fixed setting src (Source) when path is not rooted.
// - Fixed property IsCssEnabled (was returning if JS enabled, not CSS).
// - ExcludePatterns now works properly (will remove all lines matching any of the exclude patterns).
// - Extra debug patterns added (check the DebugRegexPatterns list).
//
// * 1.0
// - Initial release, check help.
//

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.Ajax.Utilities;

namespace Devv.Jasc
{
	class MainClass
	{
		#region Internal properties

		// Extra debug patterns should be placed here.
		// If RemoveDebugCalls is true, it will remove all source lines
		// matching any of these patterns before merging / compressing.
		private static List<String> DebugRegexPatterns = new List<String>()
		{
			"^\\s+.*\\.debug\\.log\\(+.*$",
			"^\\s+.*\\.debug\\.dir\\(+.*$",
			"^\\s+.*\\.debug\\.info\\(+.*$",
			"^\\s+.*\\.debug\\.count\\(+.*$",
			"^\\s+.*\\.debug\\.error\\(+.*$",
			"^\\s+.*\\.enableDebug\\(+.*$"
		};

		private static List<String> SourceFiles = new List<String>();
		private static List<String> ExcludePatterns = new List<String>();
		private static Minifier MinifierObj = new Minifier();
		private static CodeSettings CurrentCodeSettings;
		private static CssSettings CurrentCssSettings;
		private static Int32 InitialSize;

		#endregion

		#region Path and files properties

		private static String _Source = String.Empty;
		private static String _Output = String.Empty;
		private static String _ExcludePatternsPath = String.Empty;
		private static Boolean _SourceIsFileList = false;
		private static Boolean _IncludeSubDir = true;
		private static Boolean _ForceUTF8 = false;
		private static Boolean _CopyOriginal = false;

		/// <summary>
		/// Source file or directory. Settings this will auto populate the SourceFiles property.
		/// Default is the current path (where Jasc is running).
		/// </summary>
		public static String Source
		{
			get { return _Source; }
			set
			{
				String src = value;

				if (!Path.IsPathRooted(value))
				{
					src = CurrentPath + value;
				}

				if (File.Exists(src) || Directory.Exists(src))
				{
					_Source = src;
				}
				else
				{
					throw new ApplicationException("Source doesn't exist (no file and no directory).");
				}
			}
		}

		/// <summary>
		/// Output file path (including filename but NO file extension, as it will be added depending on the processing mode).
		/// Default is compressed (compressed.js for javascript and compressed.css for style sheets), on the same folder where Jasc is running.
		/// </summary>
		public static String Output
		{
			get { return _Output; }
			set
			{
				Char[] invalid = Path.GetInvalidPathChars();
				
				for (Int32 i = 0; i < invalid.Length; i++)
				{
					if (value.Contains(invalid[i].ToString()))
					{
						throw new ApplicationException("Output path has invalid char " + invalid[i]);
					}
				}

				_Output = value.Replace("\\", "/");
			}
		}

		/// <summary>
		/// Path to the file containing the exclude patterns.
		/// Default is jasc.exclude if it exists, otherwise empty.
		/// </summary>
		public static String ExcludePatternsPath
		{
			get { return _ExcludePatternsPath; }
			set { _ExcludePatternsPath = value; }
		}

		/// <summary>
		/// If true, it means the Source property is a text file with the list of files to be compressed.
		/// Default is false.
		/// </summary>
		public static Boolean SourceIsFileList
		{
			get { return _SourceIsFileList; }
			set { _SourceIsFileList = value; }
		}

		/// <summary>
		/// Include subdirectories when getting files from source (only if source is a directory).
		/// Default is false.
		/// </summary>
		public static Boolean IncludeSubDir
		{
			get { return _IncludeSubDir; }
			set { _IncludeSubDir = value; }
		}

		/// <summary>
		/// Force reading and writing of files using UTF8 encoding. If false, will use system default.
		/// Default is false.
		/// </summary>
		public static Boolean ForceUTF8
		{
			get { return _ForceUTF8; }
			set { _ForceUTF8 = value; }
		}

		/// <summary>
		/// Copy original source file. If true, a copy of the original source will be saved with the .original extension.
		/// Default is false.
		/// </summary>
		public static Boolean CopyOriginal
		{
			get { return _CopyOriginal; }
			set { _CopyOriginal = value; }
		}

		#endregion

		#region Compressor properties

		private static String _Mode = "JSCSS";
		private static Boolean _SingleLineOutput = true;
		private static Boolean _RemoveComments = true;
		private static Boolean _RemoveDebugCalls = true;
		private static Boolean _OptimizeCode = false;
		private static Boolean _DirectCompression = false;
		private static Boolean _EndSemicolons = false;

		/// <summary>
		/// Processing mode. JS for javascript, CSS for style sheets, JSCSS for both.
		/// Default is JSCSS.
		/// </summary>
		public static String Mode
		{
			get { return _Mode; }
			set { _Mode = String.IsNullOrEmpty(value) ? "JSCSS" : value.ToUpper(); }
		}

		/// <summary>
		/// Generate a single line output file if true, otherwise will make it more human readable by splitting in multiple lines.
		/// Default is true.
		/// </summary>
		public static Boolean SingleLineOutput
		{
			get { return _SingleLineOutput; }
			set { _SingleLineOutput = value; }
		}

		/// <summary>
		/// Removes source code comments from output.
		/// Default is true.
		/// </summary>
		public static Boolean RemoveComments
		{
			get { return _RemoveComments; }
			set { _RemoveComments = value; }
		}

		/// <summary>
		/// Removes lines containing debug calls (usually lines that contains .debug).
		/// Default is true.
		/// </summary>
		public static Boolean RemoveDebugCalls
		{
			get { return _RemoveDebugCalls; }
			set { _RemoveDebugCalls = value; }
		}

		/// <summary>
		/// Optimize source code: combine duplicate literals, compress variable names,
		/// removed unused variables and functions, use CSS shorthand, etc.
		/// Default is false.
		/// </summary>
		public static Boolean OptimizeCode
		{
			get { return _OptimizeCode; }
			set { _OptimizeCode = value; }
		}

		/// <summary>
		/// If true, Jasc will compress the files directly instead of merging to the output.
		/// Setting direct compression to true also makes the Output parameter useless.
		/// Default is false.
		/// </summary>
		public static Boolean DirectCompression
		{
			get { return _DirectCompression; }
			set { _DirectCompression = value; }
		}

		/// <summary>
		/// If true, Jasc will add a semicoon (;) to the end of javascript or css statements.
		/// Default is false.
		/// </summary>
		public static Boolean EndSemicolons
		{
			get { return _EndSemicolons; }
			set { _EndSemicolons = value; }
		}

		#endregion

		#region Readonly and helper properties

		/// <summary>
		/// Returns true if Source is a directory, false if it's a single file.
		/// </summary>
		public static Boolean SourceIsDirectory
		{
			get
			{
				if (String.IsNullOrEmpty(Source) || File.Exists(Source))
				{
					return false;
				}
				
				return true;
			}
		}

		/// <summary>
		/// Returns the current path (where Jasc is executing).
		/// </summary>
		public static String CurrentPath
		{
			get { return Path.GetDirectoryName(Assembly.GetCallingAssembly().Location) + "/"; }
		}

		/// <summary>
		/// Returns true if JS compressor is enabled (Mode has JS).
		/// </summary>
		public static Boolean JsEnabled
		{
			get { return Mode.Contains("JS"); }
		}

		/// <summary>
		/// Returns true if CSS compressor is enabled (Mode has CSS).
		/// </summary>
		public static Boolean CssEnabled
		{
			get { return Mode.Contains("CSS"); }
		}

		#endregion

		#region Main

		/// <summary>
		/// Main program call.
		/// </summary>
		public static void Main(String[] args)
		{
			Console.WriteLine(Environment.NewLine);

			if (args.Length > 0 && ("?,help,hilfe,ajuda".Contains(args[0].Replace("-", String.Empty))))
			{
				ShowArgumentsHelp();
			}
			else
			{
				try
				{
					Console.WriteLine(" STARTED JASC AT " + DateTime.Now.ToShortTimeString());
					Console.WriteLine(" Use \"jasc help\" to show all program options / docs.");
					Console.WriteLine();

					SetParameters(args);

					if (!String.IsNullOrEmpty(Source))
					{
						SetMinifierSettings();
						Run();
					}

					Console.WriteLine();
					Console.WriteLine(" FINISHED AT " + DateTime.Now.ToShortTimeString());
					Console.WriteLine(" Do you like Jasc? You can pay us a beer in return :-)");
					Console.WriteLine(" Donate at http://devv.com/Donate.aspx ");
					Console.WriteLine();
				}
				catch (Exception ex)
				{
					Console.WriteLine(" FATAL ERROR! " + ex);
				}
			}
		}

		/// <summary>
		/// Parses string arguments and set program parameters.
		/// </summary>
		public static void SetParameters(String[] args)
		{
			for (Int32 i = 0; i < args.Length; i++)
			{
				if (!args[i].Contains("="))
				{
					continue;
				}

				Int32 index = args[i].IndexOf("=");
				String param = args[i].Substring(0, index).ToUpper();
				String value = args[i].Substring(index + 1, args[i].Length - index - 1);
				
				switch (param)
				{
					case "MODE":
						Mode = value;
						break;
					case "SRC":
						Source = value;
						break;
					case "EX":
						ExcludePatternsPath = value;
						break;
					case "LIS":
						SourceIsFileList = GetBoolean(value);
						break;
					case "SUB":
						IncludeSubDir = GetBoolean(value);
						break;
					case "SL":
						SingleLineOutput = GetBoolean(value);
						break;
					case "DE":
						RemoveDebugCalls = GetBoolean(value);
						break;
					case "OP":
						OptimizeCode = GetBoolean(value);
						break;
					case "DC":
						DirectCompression = GetBoolean(value);
						break;
					case "ES":
						EndSemicolons = GetBoolean(value);
						break;
					case "CO":
						CopyOriginal = GetBoolean(value);
						break;
					case "UTF8":
						ForceUTF8 = GetBoolean(value);
						break;
					case "OUT":
						Output = value;
						break;
				}
			}

			if (!DirectCompression)
			{
				if (String.IsNullOrEmpty(Source))
				{
					if (SourceIsFileList)
					{
						if (File.Exists(CurrentPath + "jasc.txt"))
						{
							Source = CurrentPath + "jasc.txt";
						}
						else
						{
							Console.WriteLine(" NO SOURCE FILE SPECIFIED!");
						}
					}
					else
					{
						Source = CurrentPath;
					}
				}
				
				if (String.IsNullOrEmpty(Output))
				{
					Output = CurrentPath + "compressed";
				}
			}

			if (String.IsNullOrEmpty(ExcludePatternsPath) && File.Exists(CurrentPath + "jasc.exclude"))
			{
				ExcludePatternsPath = CurrentPath + "jasc.exclude";
			}
		}

		/// <summary>
		/// Sets the Minifier Javascript and CSS settings based on the current parameters.
		/// </summary>
		private static void SetMinifierSettings()
		{
			CurrentCodeSettings = new CodeSettings();

			CurrentCodeSettings.CollapseToLiteral = OptimizeCode;
			CurrentCodeSettings.CombineDuplicateLiterals = OptimizeCode;
			CurrentCodeSettings.EvalTreatment = EvalTreatment.MakeAllSafe;
			CurrentCodeSettings.IgnoreConditionalCompilation = !OptimizeCode;
			CurrentCodeSettings.IndentSize = 2;
			CurrentCodeSettings.InlineSafeStrings = true;
			CurrentCodeSettings.LocalRenaming = OptimizeCode ? LocalRenaming.KeepLocalizationVars : LocalRenaming.KeepAll;
			CurrentCodeSettings.MacSafariQuirks = true;
			CurrentCodeSettings.MinifyCode = OptimizeCode;
			CurrentCodeSettings.OutputMode = SingleLineOutput ? OutputMode.SingleLine : OutputMode.MultipleLines;
			CurrentCodeSettings.PreserveFunctionNames = !RemoveComments;
			CurrentCodeSettings.PreserveImportantComments = !OptimizeCode;
			CurrentCodeSettings.RemoveFunctionExpressionNames = OptimizeCode;
			CurrentCodeSettings.RemoveUnneededCode = OptimizeCode;
			CurrentCodeSettings.ReorderScopeDeclarations = OptimizeCode;
			CurrentCodeSettings.StripDebugStatements = RemoveDebugCalls;
			CurrentCodeSettings.TermSemicolons = EndSemicolons;

			CurrentCssSettings = new CssSettings();

			CurrentCssSettings.AllowEmbeddedAspNetBlocks = true;
			CurrentCssSettings.ColorNames = CssColor.Hex;
			CurrentCssSettings.CommentMode = RemoveComments ? CssComment.Hacks : CssComment.All;
			CurrentCssSettings.IndentSize = 2;
			CurrentCssSettings.MinifyExpressions = OptimizeCode;
			CurrentCssSettings.TermSemicolons = EndSemicolons;
		}

		/// <summary>
		/// Parses a Boolean value (1, y, yes, on, ok, true means TRUE, anything else means false).
		/// </summary>
		private static Boolean GetBoolean(String param)
		{
			if (String.IsNullOrEmpty(param))
			{
				return false;
			}
			
			if ("1,y,yes,true,on,ok".Contains(param.ToLower()))
			{
				return true;
			}
			
			return false;
		}

		#endregion

		#region Run compressor

		/// <summary>
		/// Run the compressor (JS, CSS, or both, depending on the mode).
		/// </summary>
		public static void Run()
		{
			GetSourceFiles();
			GetExcludePatterns();
			ShowCurrentOptions();
			
			StringBuilder js = new StringBuilder();
			StringBuilder css = new StringBuilder();

			Boolean isJsEnabled = JsEnabled;
			Boolean isCssEnabled = CssEnabled;

			InitialSize = 0;

			for (Int32 i = 0; i < SourceFiles.Count; i++)
			{
				String path = SourceFiles[i];
				String ext = Path.GetExtension(path);
				String contents = GetFileContents(path);
				
				if (!String.IsNullOrEmpty(ext))
				{
					ext = ext.Replace(".", String.Empty).ToUpper();
				}

				if (DirectCompression)
				{
					RunCompressor(contents, path);
				}
				else
				{
					if (isJsEnabled && ext.Equals("JS"))
					{
						js.Append(contents);
					}
					else if (isCssEnabled && ext.Equals("CSS"))
					{
						css.Append(contents);
					}
				}
			}

			Console.WriteLine(" Total size of original source files: " + Math.Abs(InitialSize / 1024) + "KB");

			if (!DirectCompression)
			{
				Console.WriteLine();

				if (isJsEnabled)
				{
					RunCompressor(js.ToString(), Output + ".js");
				}

				if (isCssEnabled)
				{
					RunCompressor(css.ToString(), Output + ".css");
				}
			}
		}

		/// <summary>
		/// Compresses contents (JS or CSS) and saves to the output path.
		/// </summary>
		private static void RunCompressor(String contents, String path)
		{
			String ext = Path.GetExtension(path).Replace(".", String.Empty).ToUpper();

			if (contents.Length < 3)
			{
				Console.WriteLine(" Compressed JS is empty!");

				return;
			}

			if (CopyOriginal)
			{
				using (StreamWriter sw = new StreamWriter(path + ".original", false, (ForceUTF8 ? Encoding.UTF8 : Encoding.Default)))
				{
					sw.AutoFlush = true;
					sw.Write(contents);
				}
			}

			using (StreamWriter sw = new StreamWriter(path, false, (ForceUTF8 ? Encoding.UTF8 : Encoding.Default)))
			{
				if (ext.Equals("JS"))
				{
					contents = CompressJs(contents);
				}
				else if (ext.Equals("CSS"))
				{
					contents = CompressCss(contents);
				}

				sw.AutoFlush = true;
				sw.Write(contents);
			}

			ShowMinifierOutput(path);
			Console.WriteLine(" Compressed " + ext + ": "  + Math.Abs(contents.Length / 1024) + "KB");
		}

		/// <summary>
		/// Populate the SourceFiles collection based on the Source property.
		/// </summary>
		private static void GetSourceFiles()
		{
			SourceFiles = new List<String>();

			if (SourceIsFileList)
			{
				if (File.Exists(Source))
				{
					using (StreamReader sr = new StreamReader(Source, (ForceUTF8 ? Encoding.UTF8 : Encoding.Default)))
					{
						while (sr.Peek() >= 0)
						{
							String line = sr.ReadLine();

							try
							{
								if (!String.IsNullOrEmpty(line))
								{
									if (!Path.IsPathRooted(line))
									{
										line = CurrentPath + line;
									}
	
									if (File.Exists(line))
									{
										SourceFiles.Add(line);
									}
									else
									{
										Console.WriteLine(" WARNING! File " + line + " does not exist.");
									}
								}
							}
							catch (Exception ex)
							{
								Console.WriteLine(" ERROR! Could not get file " + line);
								Console.WriteLine("        " + ex.Message);
							}
						}

						sr.Close();
					}
				}
			}
			else
			{
				if (File.Exists(Source))
				{
					SourceFiles.Add(Source);
				}
				else
				{
					SourceFiles = GetFiles(Source);
				}
			}

			Console.WriteLine(" Will process " + SourceFiles.Count + " files...");
			Console.WriteLine();
		}

		/// <summary>
		/// Gets all files within a directory, and if IncludeSubDir is true, also subdirectories.
		/// </summary>
		private static List<String> GetFiles(String initial)
		{
			List<String> result = new List<String>();
			Stack<String> stack = new Stack<String>();
			
			stack.Push(initial);
			
			while (stack.Count > 0)
			{
				String dir = stack.Pop();
				
				try
				{
					if (IncludeSubDir)
					{
						result.AddRange(Directory.GetFiles(dir));
					}
					
					foreach (String dn in Directory.GetDirectories(dir))
					{
						stack.Push(dn);
					}
				}
				catch (Exception ex)
				{
					Console.WriteLine(" ERROR! " + ex.Message);
				}
			}
			
			return result;
		}

		/// <summary>
		/// Gets a list of line patterns to be removed from the source files before compressing.
		/// </summary>
		private static void GetExcludePatterns()
		{
			ExcludePatterns = new List<String>();

			if (!String.IsNullOrEmpty(ExcludePatternsPath))
			{
				if (File.Exists(ExcludePatternsPath))
				{
					using (StreamReader sr = new StreamReader(ExcludePatternsPath, (ForceUTF8 ? Encoding.UTF8 : Encoding.Default)))
					{
						while (sr.Peek() >= 0)
						{
							String line = sr.ReadLine();

							if (!String.IsNullOrEmpty(line))
							{
								ExcludePatterns.Add(line);
							}
						}

						sr.Close();
					}
				}
				else
				{
					Console.WriteLine(" WARNING! Exclude patterns file does not exist: " + ExcludePatternsPath);
				}
			}
		}

		/// <summary>
		/// Reads the file and returns its contents.
		/// </summary>
		private static String GetFileContents(String path)
		{
			StringBuilder contents = new StringBuilder();

			try
			{
				using (StreamReader sr = new StreamReader(path, (ForceUTF8 ? Encoding.UTF8 : Encoding.Default)))
				{
					if (ExcludePatterns.Count < 1 && !RemoveDebugCalls)
					{
						contents.Append(sr.ReadToEnd());
					}
					else
					{
						List<String> removePatterns = new List<String>();
						removePatterns.AddRange(ExcludePatterns);
						removePatterns.AddRange(DebugRegexPatterns);

						while (sr.Peek() > -1)
						{
							String line = sr.ReadLine();

							foreach (String r in removePatterns)
							{
								if (r.Length > 1 && Regex.IsMatch(line, r))
								{
									line = Environment.NewLine;
									break;
								}
							}

							contents.Append(line + Environment.NewLine);
						}
					}

					sr.Close();
				}
			}
			catch (Exception ex)
			{
				String filename = Path.GetFileName(path);

				Console.WriteLine(" ERROR! Could not clean file " + filename);
				Console.WriteLine("        " + ex.Message);
			}

			InitialSize += contents.ToString().Length;

			return contents + Environment.NewLine;
		}

		/// <summary>
		/// Returns the compressed javascript (JS).
		/// </summary>
		private static String CompressJs(String contents)
		{
			if (contents.Length < 2)
			{
				return String.Empty;
			}

			return MinifierObj.MinifyJavaScript(contents, CurrentCodeSettings);
		}

		/// <summary>
		/// Returns the compressed style sheet (CSS).
		/// </summary>
		private static String CompressCss(String contents)
		{
			if (contents.Length < 2)
			{
				return String.Empty;
			}

			return MinifierObj.MinifyStyleSheet(contents, CurrentCssSettings);
		}

		#endregion

		#region Output to console and help

		/// <summary>
		/// Console: show current Jasc options.
		/// </summary>
		public static void ShowCurrentOptions()
		{
			String outputPath = Output;
			if (JsEnabled) outputPath += "(.js)";
			if (CssEnabled) outputPath += "(.css)";

			Console.WriteLine(" Mode: " + Mode);
			Console.WriteLine();

			Console.WriteLine(" Files...");
			Console.WriteLine();

			if (SourceFiles.Count == 1)
			{
				Console.WriteLine("    Source file: " + SourceFiles[0]);
			}
			else
			{
				Console.WriteLine("    Include subdirs:     " + IncludeSubDir);
				Console.WriteLine("    Source is file list: " + SourceIsFileList);
				Console.WriteLine("    Source: " + Source);
			}

			if (DirectCompression)
			{
				Console.WriteLine("    Output: direct compression!" );
			}
			else
			{
				Console.WriteLine("    Output: " + outputPath);
			}

			Console.WriteLine("    Exclude patterns file: " + (String.IsNullOrEmpty(ExcludePatternsPath) ? "EMPTY (no line patterns to exclude)" : ExcludePatternsPath));
			Console.WriteLine();
			
			Console.WriteLine(" Compression options...");
			Console.WriteLine();
			Console.WriteLine("    Single line output:      " + SingleLineOutput);
			Console.WriteLine("    Remove debug calls:      " + RemoveDebugCalls);
			Console.WriteLine("    Remove source comments:  " + RemoveComments);
			Console.WriteLine("    Optimize source code:    " + OptimizeCode);
			Console.WriteLine("    Direct compression:      " + DirectCompression);
			Console.WriteLine("    End semicolons:          " + EndSemicolons);
			Console.WriteLine("    Force UTF8 encoding:     " + ForceUTF8);

			Console.WriteLine();
		}

		/// <summary>
		/// Console: show output errors and messages from Minifier for a specified file.
		/// </summary>
		public static void ShowMinifierOutput(String path)
		{
			if (MinifierObj.ErrorList != null && MinifierObj.ErrorList.Count > 0)
			{
				Console.WriteLine(" ERROR from Ajax Minifier!");
				Console.WriteLine();

				foreach (ContextError e in MinifierObj.ErrorList)
				{
					Console.WriteLine("    File: " + Path.GetFileName(path));
					Console.WriteLine("    Position " + e.StartLine + ", " + e.StartColumn + " to " + e.EndLine + "," + e.EndColumn);
					Console.WriteLine("    Code " + e.ErrorCode + " severity " + e.Severity + " " + e.HelpKeyword);
					Console.WriteLine("    " + e.Message);
				}

				Console.WriteLine();
			}
		}

		/// <summary>
		/// Console: show the program help.
		/// </summary>
		public static void ShowArgumentsHelp()
		{
			const String SEP = "---------------------------------------------------------------";
			
			Console.WriteLine(" JASC HELP");
			Console.WriteLine(SEP);
			
			Console.WriteLine(" The arguments should be passed as ARGUMENT=VALUE");
			Console.WriteLine();
			
			Console.WriteLine(" - mode  Processing mode can JS for javascript, CSS for style sheets,");
			Console.WriteLine("         or JSCSS for both. Default is JSCSS.");
			Console.WriteLine();
			Console.WriteLine(" - src   File or directory of files to compress.");
			Console.WriteLine("         Default is the current directory if lis=false.");
			Console.WriteLine("         If lis=true (source is file list), default is jasc.txt.");
			Console.WriteLine();
			Console.WriteLine(" - lis   If true it means the source is a text file containing a");
			Console.WriteLine("         list of files to be compressed.");
			Console.WriteLine("         Default is false.");
			Console.WriteLine();
			Console.WriteLine(" - sub   Include subdirectories when getting source files?");
			Console.WriteLine("         Default is true.");
			Console.WriteLine();
			Console.WriteLine(" - ex    Path to the file containing the exclude line regex list.");
			Console.WriteLine("         Default is jasc.exclude (if it exists).");
			Console.WriteLine();
			Console.WriteLine(" - sl    Output to a single line file. If false, code will be split");
			Console.WriteLine("         to multiple lines to make it more readable.");
			Console.WriteLine("         Default is true.");
			Console.WriteLine();
			Console.WriteLine(" - de    Remove debug calls (lines with .debug.log and .debug.count).");
			Console.WriteLine("         Default is true.");
			Console.WriteLine();
			Console.WriteLine(" - op    Optimize source code. Use it with care!");
			Console.WriteLine("         JS: combine variables and literals, remove unreachable code.");
			Console.WriteLine("         CSS: combine similar styles, use CSS shorthand.");
			Console.WriteLine("         Default is false.");
			Console.WriteLine();
			Console.WriteLine(" - dc    Direct compression, if true Jasc will compress the source");
			Console.WriteLine("         files (each one) directly instead of merging everything to");
			Console.WriteLine("         a single compressed output.");
			Console.WriteLine("         Default is false.");
			Console.WriteLine();
			Console.WriteLine(" - es    End semicolons, if true Jasc will add a semicolon to the");
			Console.WriteLine("         end of the javascript or css statements.");
			Console.WriteLine("         Default is false.");
			Console.WriteLine();
			Console.WriteLine(" - co    Copy original, if true it will save a copy of the original");
			Console.WriteLine("         source file(s) with the extension .original.");
			Console.WriteLine("         Default is false.");
			Console.WriteLine();
			Console.WriteLine(" - utf8  Force reading and writing files using UTF8 encoding.");
			Console.WriteLine("         Default is false.");
			Console.WriteLine();
			Console.WriteLine(" - out   The output path WITHOUT extension. Default is compressed:");
			Console.WriteLine("         compressed.js for javascript, compressed.css for css.");
			Console.WriteLine();
			Console.WriteLine();
			
			Console.WriteLine(" Examples:");
			Console.WriteLine();
			
			Console.WriteLine(" jasc.exe");
			Console.WriteLine(" This will compress all javascript and css files in the current");
			Console.WriteLine(" folder, including subfolders, and save the output to");
			Console.WriteLine(" compressed.js and compressed.css.");
			Console.WriteLine();

			Console.WriteLine(" jasc.exe op=1 urf8=1");
			Console.WriteLine(" Same as example above (compress all files in the current folder)");
			Console.WriteLine(" but with code optimization and reading/writing using UTF8.");
			Console.WriteLine();

			Console.WriteLine(" jasc.exe lis=1");
			Console.WriteLine(" This will compress all javascript and css files listed on");
			Console.WriteLine(" the default file jasc.txt at the current folder.");
			Console.WriteLine(" Save to compressed.js and compressed.css.");
			Console.WriteLine();

			Console.WriteLine(" jasc.exe lis=1 src=//Proj/jslist.txt out=//Proj/devv.min");
			Console.WriteLine(" Compresses all files listed in jslist.txt and saves the");
			Console.WriteLine(" compressed result to the file //Proj/devv.min.js.");
			Console.WriteLine();
			
			Console.WriteLine(" jasc.exe mode=JS src=C:\\Projects\\Devv out=C:\\Scripts.js");
			Console.WriteLine(" Compresses all javascript files inside C:\\Projects\\Devv");
			Console.WriteLine(" and saves the compressed result to C:\\Scripts.js.");
			Console.WriteLine();
			
			Console.WriteLine(" jasc.exe src=C:\\Projects\\Devv\\Devv.js sl=0 op=1");
			Console.WriteLine(" Compresses the C:\\Projects\\Devv\\Devv.js file, doing also");
			Console.WriteLine(" code optimizations and output to multiple lines.");
			Console.WriteLine();
			
			Console.WriteLine(" jasc.exe sub=0 ex=//Dev/proj/excludelines.txt");
			Console.WriteLine(" This will compress all javascript and css files in the current");
			Console.WriteLine(" folder but not subfolders. Remove all lines that contains");
			Console.WriteLine(" patterns included in the //Dev/proj/excludelines.txt file.");
			Console.WriteLine();
			
			Console.WriteLine(SEP);
			Console.WriteLine(" For more information, visit http://jasc.codeplex.com");
			Console.WriteLine(" Like Jasc? Visit us at http://devv.com");
			Console.WriteLine(SEP);
		}
		
		#endregion
	}
}